﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;

namespace Pulsar
{
    public partial class Form1 : Form
    {
        private BackgroundWorker AsyncWorkerShow;

        ManualResetEvent _busy;

        PulsarScenario sc;

        public Form1()
        {
            InitializeComponent();
            this.CenterToScreen();

            AsyncWorkerShow = new BackgroundWorker();
            AsyncWorkerShow.WorkerReportsProgress = true;
            AsyncWorkerShow.WorkerSupportsCancellation = true;

            AsyncWorkerShow.DoWork += new DoWorkEventHandler(AsyncWorkerShow_DoWork);
            AsyncWorkerShow.RunWorkerCompleted += new RunWorkerCompletedEventHandler(AsyncWorkerShow_RunWorkerCompleted);
            AsyncWorkerShow.ProgressChanged += new ProgressChangedEventHandler(AsyncWorkerShow_ProgressChanged);
            buttonStop.Enabled = false;

            _busy = new ManualResetEvent(true);
            toolStripStatusLabel1.Text = "Ready!";

            //intial configuration
            PulsarGlobalOptions.ParallelPortAddress = 888;
            PulsarGlobalOptions.ComPortName = "COM5";
            PulsarGlobalOptions.ComPortBaudRate = 115200;
            PulsarGlobalOptions.Tagger = new BulkTagger();//PulsarSerialPort();
        }

        void AsyncWorkerShow_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState is int && (int)e.UserState == 1) //progressbar 1
            {
                progressBar1.Value = e.ProgressPercentage;
            }
            else if (e.UserState is int && (int)e.UserState == 2) ///progressbar 2
            {
                progressBar2.Value = e.ProgressPercentage;
            }
            else if (e.UserState is int && (int)e.UserState == 3) //left listbox
            {
                listBox1.SelectedIndex = e.ProgressPercentage;
            }
            else if (e.ProgressPercentage ==-1 && e.UserState is string) //repetions label
            {
                label4.Text = (string)e.UserState;
            }
            else if (e.UserState is Image) //stimulus image
            {
                toolStripStatusLabel1.Text = "Presentation has started...";
                pictureBox1.Image = (Image)e.UserState;
            }
        }

        void AsyncWorkerShow_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {

                if (e.Error is SerialPortExcpetion)
                {
                    MessageBox.Show("Tagging initialization has failed. Please check configuration or set the tagging to \"None\"", "Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    Options of = new Options();
                    of.Show();
                }
                else
                {
                    MessageBox.Show(e.Error.Message, "Error");
                    toolStripStatusLabel1.Text = "Presentation of stimuli has completed with errors!";
                }
            }
            else
            {
                if (e.Cancelled)
                {
                    toolStripStatusLabel1.Text = "Presentation has been cannceled.";
                }
                else
                {
                    toolStripStatusLabel1.Text = "Finished presenting!";
                }
            }

            PulsarGlobalOptions.Tagger.Close();

            buttonStart.Enabled = true;
            progressBar1.Value = 0;
            progressBar2.Value = 0;
        }

        void AsyncWorkerShow_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bwAsync = sender as BackgroundWorker;

            PulsarGlobalOptions.Tagger.Open();

            PulsarGlobalOptions.Tagger.Write(0);//init

            int eventCount = 0;
            foreach (PulsarEvent pevent in sc.Events) //loop over classes
            {
                _busy.WaitOne(Timeout.Infinite);

                bwAsync.ReportProgress(-1, pevent.Image); //update image
                bwAsync.ReportProgress(eventCount, 3); //update left list box

                int progressBarChange = 16;

                for (int i = 0; i < pevent.Repetions; i++)
                {
                    bwAsync.ReportProgress(-1, (i + 1).ToString() + " / " + pevent.Repetions.ToString());
                    _busy.WaitOne(Timeout.Infinite);

                    //Tagging start
                    PulsarGlobalOptions.Tagger.Write(pevent.StimulusStartCode);

                    bwAsync.ReportProgress(0, 1);
                    bwAsync.ReportProgress(0, 2);

                    //stimuli progress bar
                    int timeElapsed = 0;

                    int timePerProgressUpdate = (int)((double)sc.SingleStimulusTimeDuration / progressBarChange);

                    while (timeElapsed + 20 < sc.SingleStimulusTimeDuration)
                    {
                        if (bwAsync.CancellationPending) { e.Cancel = true; return; }
                        _busy.WaitOne(Timeout.Infinite);

                        System.Threading.Thread.Sleep(timePerProgressUpdate);
                        timeElapsed += timePerProgressUpdate;

                        double progress = ((double)timeElapsed / (double)sc.SingleStimulusTimeDuration) * 100;

                        bwAsync.ReportProgress((int)progress, 1);
                    }

                    bwAsync.ReportProgress(100, 1);

                    //Tagging end
                    PulsarGlobalOptions.Tagger.Write(pevent.StimulusEndCode);

                    //pause bar
                    timeElapsed = 0;

                    timePerProgressUpdate = (int)((double)sc.PauseBetweenStimuliDuration / progressBarChange);

                    while (timeElapsed + 20 < sc.PauseBetweenStimuliDuration)
                    {
                        if (bwAsync.CancellationPending) { e.Cancel = true; return; }
                        _busy.WaitOne(Timeout.Infinite);

                        System.Threading.Thread.Sleep(timePerProgressUpdate);
                        timeElapsed += timePerProgressUpdate;

                        double progress = ((double)timeElapsed / (double)sc.PauseBetweenStimuliDuration) * 100;

                        bwAsync.ReportProgress((int)progress, 2);
                    }

                    bwAsync.ReportProgress(100, 2);

                    System.Threading.Thread.Sleep(800); //additional sleep
                }

                eventCount++;
                _busy.WaitOne(Timeout.Infinite);

                PulsarGlobalOptions.Tagger.Write(sc.BetweenStimulusCode);
                System.Threading.Thread.Sleep(800); 
                PulsarGlobalOptions.Tagger.Write(0);//init
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            LoadScenario();

            if (sc != null && sc.Events!=null)
            listBox1.DataSource = (from pevent in sc.Events
                                  select pevent.Name).ToArray();
        }

        public void LoadScenario()
        {
            sc = new PulsarScenario();

            sc.SingleStimulusTimeDuration = 4000;
            sc.PauseBetweenStimuliDuration = 2000;
            sc.BetweenStimulusCode = 2;

            string ImageFolder = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Images";
            if (!Directory.Exists(ImageFolder))
            {
                MessageBox.Show("No image folder detected! '" + ImageFolder +"' is invalid!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                string[] all = Directory.GetFiles(ImageFolder);

                if (all.Length > 0)
                {
                    try
                    {
                        Array.Sort(all);

                        short i = 0;
                        var q = from one in all
                                where !one.ToLower().EndsWith("thumbs.db")
                                let image = new Bitmap(one)
                                select new PulsarEvent
                                {
                                    Name = "Stimulus" + ++i,
                                    Image = image,
                                    StimulusStartCode = 1,
                                    StimulusEndCode = 0,
                                    Repetions = 3
                                };

                        sc.Events = q.ToArray();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Could not load stimuli: " + ex.Message + ". Make sure the folder contains only pictures and no hidden files!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
                else
                {
                    MessageBox.Show("The selected image folder contains no files!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            AsyncWorkerShow.RunWorkerAsync();

            buttonStop.Enabled = true;
            buttonStart.Enabled = false;
            buttonPause.Enabled = true;
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            if (AsyncWorkerShow.IsBusy)
            {
                _busy.Set(); //resume

                buttonStop.Enabled = false; //so that the user can not click multipe times
                buttonPause.Enabled = false;
                buttonResume.Enabled = false;

                AsyncWorkerShow.CancelAsync();
            }
        }

        private void buttonPause_Click(object sender, EventArgs e)
        {
            buttonPause.Enabled = false;
            buttonResume.Enabled = true;
            _busy.Reset();
            toolStripStatusLabel1.Text = "Presentation has been paused.";

        }

        private void buttonResume_Click(object sender, EventArgs e)
        {
            _busy.Set();
            buttonPause.Enabled = true;
            buttonResume.Enabled = false;
            toolStripStatusLabel1.Text = "Presentation has resumed and in progress ...";
        }

        private void optionsToolStripMenuItem2_Click(object sender, EventArgs e)
        {
            Options o = new Options();
            o.Show();
        }

        private void editScenarioToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            EditScenario ef = new EditScenario(ref this.sc);
            ef.Show();
        }

        private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            AsyncWorkerShow.CancelAsync();

            Application.Exit();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            About af = new About();
            af.Show();
        }
    }
}
